﻿using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Experimentor
{
    class MainWorker : BackgroundWorker
    {
        public ManualResetEvent mre;

        public MainWorker(DoWorkEventHandler handler)
        {
            this.WorkerSupportsCancellation = false;
            this.WorkerReportsProgress = false;
            this.DoWork += handler;
            this.mre = new ManualResetEvent(false);
        }
    }

    class SequenceWorker : BackgroundWorker
    {
        private form_ExperimRun parent;
        private int seqOffset;
        public ManualResetEvent mre;
      
        public SequenceWorker(form_ExperimRun parent, int seqOffset)
        {
            this.WorkerSupportsCancellation = true;
            this.parent = parent;
            this.seqOffset = seqOffset;
            this.DoWork += this.sequenceWorker_DoWork;
            this.RunWorkerCompleted += this.sequenceWorker_RunWorkerCompleted;
            this.mre = new ManualResetEvent(false);
        }

        private void sequenceWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.parent.Invoke((MethodInvoker)delegate { this.parent.doSets(0); });
            mre.WaitOne();
        }
        
        private void sequenceWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                this.parent.Invoke((MethodInvoker)delegate { this.parent.doSequences(this.seqOffset+1); });
            }
            catch (InvalidOperationException ioe)
            {
                Console.WriteLine(ioe.Message);
            }
        }
    }

    class SetWorker : BackgroundWorker
    {
        private form_ExperimRun parent;
        private int setOffset;
        public ManualResetEvent mre;

        public SetWorker(form_ExperimRun parent, int setOffset)
        {
            this.WorkerSupportsCancellation = true;
            this.parent = parent;
            this.setOffset = setOffset;
            this.DoWork += this.setWorker_DoWork;
            this.RunWorkerCompleted += this.setWorker_RunWorkerCompleted;
            this.mre = new ManualResetEvent(false);
        }

        private void setWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.parent.Invoke((MethodInvoker)delegate { this.parent.doSlides(0); });
            mre.WaitOne();
        }

        private void setWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                this.parent.Invoke((MethodInvoker)delegate { this.parent.saveSet(this.setOffset); });
                this.parent.Invoke((MethodInvoker)delegate { this.parent.doSets(this.setOffset + 1); });
            }
            catch (InvalidOperationException ioe)
            {
                Console.WriteLine(ioe.Message);
            }
        }
    }

    public class SeqPartWorker : BackgroundWorker
    {
        private form_ExperimRun parent;
        private int seqPartOffset;
        public ManualResetEvent mre;
        private List<SeqPartWorkerListener> listener;

        public SeqPartWorker(form_ExperimRun parent, int seqPartOffset)
        {
            this.WorkerSupportsCancellation = true;
            this.parent = parent;
            this.seqPartOffset = seqPartOffset;
            this.DoWork += this.seqPartWorker_DoWork;
            this.RunWorkerCompleted += this.seqPartWorker_RunWorkerCompleted;
            this.mre = new ManualResetEvent(false);
            this.listener = new List<SeqPartWorkerListener>();
        }

        private void seqPartWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (this.listener.Count > 0) mre.WaitOne();
        }

        private void seqPartWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            foreach (SeqPartWorkerListener list in this.listener) list.disableParent();
            try
            {
                this.parent.Invoke((MethodInvoker)delegate { this.parent.doSlides(this.seqPartOffset + 1); });
            }
            catch (InvalidOperationException ioe)
            {
                Console.WriteLine(ioe.Message);
            }
        }

        public void registerListener(SeqPartWorkerListener list)
        {
            this.listener.Add(list);
        }

    }

    public interface SeqPartWorkerListener
    {
        void disableParent();
    }

    public class WaitWorker : BackgroundWorker, SeqPartWorkerListener
    {
        private SeqPartWorker parent;
        private int sleepms;

        public WaitWorker(SeqPartWorker parent, int sleepms)
        {
            this.WorkerSupportsCancellation = true;
            this.parent = parent;
            this.sleepms = sleepms;
            this.DoWork += this.waitWorker_DoWork;
            this.RunWorkerCompleted += this.waitWorker_RunWorkerCompleted;
            this.parent.registerListener(this);
        }

        private void waitWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(this.sleepms);
        }

        private void waitWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.parent != null) this.parent.mre.Set();
        }

        public void disableParent()
        {
            this.parent = null;
        }
    }
}
